package middleware

import (
	"context"

	"github.com/emicklei/go-restful/v3"
	"github.com/infraboard/mcube/v2/http/label"
	"github.com/infraboard/mcube/v2/ioc"
	"github.com/infraboard/mcube/v2/ioc/config/application"
	"github.com/infraboard/mcube/v2/ioc/config/gorestful"
	"github.com/infraboard/mcube/v2/ioc/config/log"
	"github.com/rs/zerolog"
	"gitlab.com/go-course-project/go15/devcloud-mini/maudit/apps/event"

	"github.com/infraboard/mcube/v2/ioc/config/kafka"
)

func init() {
	ioc.Config().Registry(&Client{})
}

// 1. 收集用户访问的接口数据
// 2. 发送到审计中心的topic
type Client struct {
	ioc.ObjectImpl

	log *zerolog.Logger
	// 审计中心Kafka topic名称配置
	MauditTopicName string `json:"maudit_topic_name" yaml:"maudit_topic_name" toml:"maudit_topic_name" env:"MAUDIT_TOPIC_NAME"`
}

func (c *Client) Name() string {
	return "maudit_client"
}

// 独立对象，应该具有完备的功能
// 我映入了中间件了后, 都不用做，一起就可以正常工作
// 在初始化的时候 就需要把中间件注入到 全局Router里面
func (c *Client) Init() error {
	c.log = log.Sub("maudit_client")

	c.log.Debug().Msgf("maduit topic name: %s", c.MauditTopicName)
	// 加到Root Router里面
	ws := gorestful.RootRouter()
	ws.Filter(c.Audit())
	return nil
}

func (c *Client) Audit() restful.FilterFunction {
	return func(r1 *restful.Request, r2 *restful.Response, fc *restful.FilterChain) {
		// 如何在中间件里面获取路有条目信息
		//  curl  localhost:8020/api/cmdb/v1/secret
		sr := r1.SelectedRoute()
		md := NewMetaData(sr.Metadata())

		if md.GetBool(label.Audit) {
			// 获取当前是否需要审计
			e := event.NewEvent()
			// ioc 里面获取当前应用的名称
			e.Service = application.Get().AppName
			e.ResourceType = md.GetString(label.Resource)
			e.Action = md.GetString(label.Action)
			// {id} /:id
			e.ResourceId = r1.PathParameter("id")
			e.UserAgent = r1.Request.UserAgent()
			e.Extras["method"] = sr.Method()
			e.Extras["path"] = sr.Path()
			e.Extras["operation"] = sr.Operation()

			defer func() {
				// 补充处理后的数据
				e.StatusCode = r2.StatusCode()
				// 发送给topic, 使用这个中间件的使用者，需要配置kafka
				err := kafka.Producer(c.MauditTopicName).WriteMessages(context.Background(), e.ToKafkaMessage())
				if err != nil {
					c.log.Error().Msgf("send message error, %s", err)
				}
			}()
		}

		// 路有给后续逻辑
		fc.ProcessFilter(r1, r2)
	}
}

func NewMetaData(data map[string]any) *MetaData {
	return &MetaData{
		data: data,
	}
}

type MetaData struct {
	data map[string]any
}

func (m *MetaData) GetString(key string) string {
	if v, ok := m.data[key]; ok {
		return v.(string)
	}
	return ""
}

func (m *MetaData) GetBool(key string) bool {
	if v, ok := m.data[key]; ok {
		return v.(bool)
	}
	return false
}
